#ifndef __CLI_PARSE__SECTOR_H__
#define __CLI_PARSE__SECTOR_H__


#include "flags.h"
#include "structs.h"   // e3dBoundingBox
#include "constants.h" // M_PI

#include "object.h"    // cli_parse::Object

#include "../../Epsilon/STL.h"
#include "../../Epsilon/Epsilon3D/driver.h" // e3dRenderStates
using namespace e3dRenderStates;

#include <math.h> //tan

#include <vector>

class e3dIndexCache;
class e3dVertexCache;

class eTB_Polygon;

namespace cli_parse {
  class RenderBatch;
  class Actor;
};

namespace Underlight
{
  class Line;
  class LineList;

  class Sector;
  class SectorWall : public cli_parse::Object {
  };

  class Surface : public cli_parse::Object
  {
  public:
    Surface (float a_base_height, float a_slope, bool no_stretch) {
      baseline           = NULL;
      slope              = (float)tan (a_slope * M_PI / 180);
      base_height        = a_base_height;
      no_texture_stretch = no_stretch;
    }

    float height_at (float x, float y);

    void  set_baseline (Line* base) { baseline = base; };
    Line* Baseline     (void)       { return baseline; };
    float Slope        (void)       { return slope;    };

    void tex_coord_at (float x, float y, int tex_id,
                        const Sector* sec,
                         bool floor,
                           float& tex_u,
                           float& tex_v) const;

    /*
    void tex_coord_offset_at (float x, float y, int tex_id, const Sector* sec, bool floor, float& tex_u, float& tex_v, float x_off, float y_off);
    */

    size_t size (void) const;

  private:
    float    slope;
    float    base_height;
    Line*    baseline;
    bool     no_texture_stretch;
  };

  class Sector : public cli_parse::Object
  {
  public:
    class Flat : public cli_parse::Object
    {
    public:
               Flat (PrimitiveType type);
      virtual ~Flat (void);

      size_t size (void) const;

    //protected:
      e3dIndexCache* floor;
      e3dIndexCache* ceil;
      PrimitiveType  primitive_type;
    };

  public:
             Sector (int id_ = -1);
    virtual ~Sector (void);

    float CeilHt (float x, float y) const
    {
      return ceiling_height + (ceil ? ceil->height_at (x, y) : 0.0f);
    }

    float FloorHt (float x, float y) const
    {
      return floor_height + (floor ? floor->height_at (x, y) : 0.0f);
    }

    void TexCoordFloor (float x, float y, float& u, float& v) const
    {
      if (! floor)
      {
        // Construct a dummy floor surface for non-sloped sectors
        Surface _floor (floor_height, 0.0f, (flags & SECTOR_NO_STRETCH_FLOOR));
        _floor.tex_coord_at (x, y, floor_bitmap, this, true, u, v);
      } else {
        floor->tex_coord_at (x, y, floor_bitmap, this, true, u, v);
      }
    }

    void TexCoordCeil (float x, float y, float& u, float& v) const
    {
      if (! ceil)
      {
        // Construct a dummy ceiling surface for non-sloped sectors
        Surface _ceil (ceiling_height, 0.0f, (flags & SECTOR_NO_STRETCH_CEILING));
        _ceil.tex_coord_at (x, y, ceiling_bitmap, this, false, u, v);
      } else {
        ceil->tex_coord_at (x, y, ceiling_bitmap, this, false, u, v);
      }
    }

    bool SlopingFloor   (void) const { return (floor && floor->Slope ()); };
    bool SlopingCeiling (void) const { return (ceil  &&  ceil->Slope ()); };

    int NumberOfEdges (void) const;

    // Is there any reason to distinguish between edges and vertices?
    int NumberOfVertices (void) const
    {
      return NumberOfEdges ();
    }

    // A sector is "simple" if no other sectors occupy the
    //  sector's surface area.
    bool IsSimple (LineList* pUsed = NULL);

    // Certain sectors are not intended to be drawn...
    //
    //  * If this function returns true, we can cull the sector and
    //    skip load-time geometry processing.
    bool IsNoDraw (void);

    bool Contains    (Line* line)       const;
    bool PointInside (float x, float y) const;

    Line* FindNextLine (float x, float y, LineList& used, Line* old = NULL);
    Line* FindPrevLine (float x, float y, LineList& used, Line* old = NULL);

    void Unload (void);

    virtual void Tesselate (void);

    void TesselateContour    (Line* first);
    void TesselateContours   (void);

    void CalculateSurfaces   (bool simple);
    void CalculateWalls      (void);


    void CacheBatches        (void);
    void CachePickingBatches (void);

    VertexData*  FindFloorBaseLine (bool facing, bool from = true) const;
    static float FindHighestFloor  (Sector* sec1, Sector* sec2);

    VertexData*  FindCeilingBaseLine (bool facing, bool from = true) const;
    static float FindHighestCeiling  (Sector* sec1, Sector* sec2);

    void              AddActor (cli_parse::Actor* actor);
    cli_parse::Actor* GetActor (int id) const;

    Line* GetLine (int id);

    /**
     * Copy the sector's states
     *
     *  * NOTE: This DOES NOT copy the sector's surfaces,
     *          index caches, walls, render batches or contours.
     *
     */
    void CopyStates (Sector* sector);

    class Wall : public SectorWall
    {
    public:
               Wall (Line* pLine, bool floor);
      virtual ~Wall (void);

      /* TODO: Inline*/
      int GetTexture (void); /*
        return (is_floor ? parent_line->fwall_bitmap : parent_line->cwall_bitmap);
      }
      */

      bool IsFloor (void) { return is_floor; };

      ul_tex_coords_t CalculateTexCoords (int type);

      void InitTexture (void);

      bool Calculate (e3dVertexCache* vcache);

      size_t size (void) const;

      e3dIndexCache*    icache;
      Line*             parent_line;
      bool              is_floor;

      short             texture;
      short             texture_width;
      short             texture_height;

      float             u_length;        // length over which a texture is stretched
      int               u_mask; 
      int               u_offset;
      int               u_max;
      int               u_min;

      float             v_stretch_ratio;  // ratio determines how much to stretch texture (1.0 == no stretching)
      int               v_mask;
      float             v_offset;

      float             to_sec_height;
      float             from_sec_height;
      float             height_origin;

      ul_wall_heights_t wall_ht;

      eTB_Polygon*      polygon;
    };

  //private:
    int id;

    short ceiling_bitmap; // in BITMAPINFO array, the top texture.
    short floor_bitmap;   // the bottom texture.

    short floor_height;
    short ceiling_height;

    short height_offset; // height offset for actors in this sector


    /// TODO:
    ///
    /// The texturemapping is done in vertical scanlines for the walls and
    ///    horizontal scanlines for the floor and ceiling. Since these are
    ///    lines of constant Z, the stepwidth in the textures is linear.

    /// The shading of the textures is done via lookup in a palette table.
    ///  * Which table is used for the whole line is calculated from the
    ///    distance and the light level in the sector.


    int light_level;

    int flags;

    float floor_slope_angle;
    float ceiling_slope_angle;

    Surface* floor;
    Surface* ceil;

    short xanim_top; // pixels per frame animation for floor and ceiling
    short yanim_top;
    short xanim_bottom;
    short yanim_bottom;

    enum {
      SECTOR_ANIMATED_FLOOR = 0x01,
      SECTOR_ANIMATED_CEIL  = 0x02,
      SECTOR_ANIMATED_LIGHT = 0x04,

//      SECTOR_SIMPLE         = 0x10,
//      SECTOR_NODRAW         = 0x20,
    };

    bool IsAnimatedFloor   (void) const { return render_flags & SECTOR_ANIMATED_FLOOR; };
    bool IsAnimatedCeiling (void) const { return render_flags & SECTOR_ANIMATED_CEIL;  };
    bool IsAnimatedLight   (void) const { return render_flags & SECTOR_ANIMATED_LIGHT; };

    int  render_flags; // Animated Floor, Animated Ceiling, Animated Light, Simple, No Draw

    Line* firstline;  // first line of linedefs for this sector

    std::vector<Flat*> flats;

    signed char simple;    // Cache the result of IsSimple (...)
    signed char nodraw;    // Cache the result of IsNoDraw (...)

    std::vector<Wall*> walls;
    std::vector<cli_parse::RenderBatch*> batches;
    std::vector<cli_parse::RenderBatch*> picking_batches; ///< Special batches that will
                                                          ///  setup the framebuffer for
                                                          ///  pixel-perfect picking.

    std::vector<Sector*> contours; ///< Sectors that form the contour of this one

    std::vector<cli_parse::Actor*>  actors;

    e3dBoundingBox bound;

    virtual size_t size (void) const;
  };

};

#endif /* __CLI_PARSE__SECTOR_H__ */
